home *** CD-ROM | disk | FTP | other *** search
- # @(#)idol.iol 6.30 (3/14/90)
- #
- # Idol: Icon-derived object language, version 6.30
- #
- # SYNOPSIS:
- #
- # idol -install
- # idol prog[.iol] ... [-x args ]
- # prog
- #
- # FILES:
- #
- # ./prog.iol : source file
- # ./prog.icn : Icon code for non-classes in prog.iol
- # ./idolcode.env/i_object.* : Icon code for the universal object type
- # ./idolcode.env/classname.icn : Icon files are generated for each class
- # ./idolcode.env/classname.u[12] : translated class files
- # ./idolcode.env/classname : class specification/interface
- #
- # SEE ALSO:
- #
- # "Programming in Idol: An Object Primer"
- # (U of Arizona Dept of CS Technical Report #90-10)
- # serves as user's guide and reference manual for Idol
- #
- ### Global variables
- #
- # FILES : fin = input (.iol) file, fout = output (.icn) file
- # CSETS : alpha = identifier characters, nonalpha = everything else
- # alphadot = identifiers + '.'
- # white = whitespace, nonwhite = everything else
- # TAQUES : classes in this module
- # FLAGS : comp if we should try to make an executable from args[1]
- # strict if we should generate paranoic encapsulation protection
- # loud if Idol should generate extra console messages
- # exec if we should run the result after translation
- # LISTS : links = names of external icon code to link to
- # imports = names of external classes to import
- # compiles = names of classes which need to be compiled
- #
- global fin,fout,fName,fLine,alpha,alphadot,white,nonwhite,nonalpha
- global classes,comp,exec,strict,links,imports,loud,compiles
-
- #
- # initialize global variables
- #
- procedure initialize()
- loud := 1
- comp := 0
- alpha := &ucase ++ &lcase ++ '_' ++ &digits
- nonalpha := &cset -- alpha
- alphadot := alpha ++ '.'
- white := ' \t\014'
- nonwhite := &cset -- white
- classes := taque()
- links := []
- imports := []
- compiles := []
- sysinitialize()
- end
-
- procedure main(args)
- initialize()
- if *args = 0 then write("usage: idol files...")
- else {
- every i := 1 to *args do {
- if \exec then next # after -x, args are for execution
- if args[i][1] == "-" then {
- case map(args[i]) of {
- "-c" : {
- sysok := &null
- if comp = 0 then comp := -1 # don't make exe
- }
- "-install": return install(args[1:i+1])
- "-quiet" : loud := &null
- "-strict" : strict := 1
- "-s" : sysok := &null
- "-t" : comp := -2 # don't translate
- "-version": return write("Idol version 6.30 of 3/14/90") & 0
- "-x" : exec := i
- }
- }
- else if args[i][find(".cl",args[i]):0] := "" then push(imports,args[i])
- else if args[i][find(".icn",args[i]):0] := "" then {
- push(links,args[i])
- icont(" -c "||args[i])
- }
- else if args[i][find(".u1",args[i]):0] := "" then push(links,args[i])
- else if (args[i][find(".iol",args[i]):0] := "") |
- tryopen(args[i]||".iol","r") then {
- /exe := i
- args[i][find(".iol",args[i]):0] := ""
- /fout := sysopen(args[i]||".icn","w")
- readinput(args[i]||".iol",1)
- } else {
- #
- # let's go out and look for an appropriate .icn, .u1 or class file!
- #
- if tryopen(args[i]||".icn","r") then {
- push(links,args[i])
- icont(" -c "||args[i])
- }
- else if tryopen(args[i]||".u1") then push(links,args[i])
- else if tryenvopen(args[i]) then push(imports,args[i])
- }
- }
- gencode()
- close(\fout)
- if comp = 1 then makeexe(args,exe)
- }
- end
-
- #
- # gencode first generates specifications for all defined classes
- # It then imports those classes' specifications which it needs to
- # compute inheritance. Finally, it writes out all classes' .icn files.
- #
- procedure gencode()
- if \loud then write("Class import/export:")
- #
- # export specifications for each class
- #
- every cl := classes$foreach_t() do cl$writespec()
- #
- # import class specifications, transitively
- #
- repeat {
- added := 0
- every super:= ((classes$foreach_t())$foreachsuper() | !imports) do{
- if /classes$lookup(super) then {
- added := 1
- fname := filename(super)
- readinput(envpath(fname),2)
- if /classes$lookup(super) then halt("can't import class '",super,"'")
- writesublink(fname)
- }
- }
- if added = 0 then break
- }
- #
- # compute the transitive closure of the superclass graph
- #
- every (classes$foreach_t())$transitive_closure()
- #
- # generate output
- #
- if \loud then write("Generating code:")
- writesublink("i_object")
- every s := !links do writelink(s)
- write(fout)
- every out := $!classes do {
- name := filename(out$name())
- out$write()
- put(compiles,name)
- writesublink(name)
- }
- if *compiles>0 then cdicont(compiles)
- end
-
- #
- # a class defining objects resulting from parsing lines of the form
- # tag name ( field1 , field2, ... )
- # If the constructor is given an argument, it is passed to self$read
- #
- class declaration(public name,fields,tag)
- #
- # parse a declaration string into its components
- #
- method read(decl)
- decl ? {
- # get my tag
- tab(many(white))
- if not (self.tag := =("procedure"|"class"|"method"|"record")) then
- halt("declaration/read can't parse decl ",decl)
- tab(many(white))
- # get my name
- if not (self.name := tab(many(alpha))) then
- halt("declaration/read can't parse decl ",decl)
- # get my fields
- if not tab(find("(")+1) then
- halt("declaration/read can't parse decl ",decl)
- tab(many(white))
- self.fields := classFields()
- if not (self.fields$parse(tab(find(")")))) then
- halt("declaration/read can't parse decl ",decl)
- }
- end
-
- #
- # write a declaration; at the moment, only used by records
- #
- method write(f)
- write(f,self$String())
- end
- #
- # convert self to a string
- #
- method String()
- return self.tag || " " || self.name || "(" || self.fields$String() || ")"
- end
- initially
- if \self.name then self$read(self.name)
- end
-
- #
- # class body manages a list of strings holding the code for
- # procedures/methods/classes
- #
- class body(fn,ln,text)
- method read()
- self.fn := fName
- self.ln := fLine
- self.text := []
- while line := readln() do {
- put(self.text, line)
- line ? { tab(many(white)); if ="end" & &pos > *line then return }
- }
- halt("body/read: eof inside a procedure/method definition")
- end
- method write(f)
- if \self.ln then write(f,"#line ",self.ln," \"",self.fn,"\"")
- every write(f,$!self)
- end
- method delete()
- return pull(self.text)
- end
- method size()
- return (*\ (self.text)) | 0
- end
- method foreach()
- if t := \self.text then suspend !self.text
- end
- end
-
- #
- # a class defining operations on classes
- #
- class class : declaration (supers,methods,text,imethods,ifields,glob)
- # imethods and ifields are all lists of these:
- record classident(class,ident)
-
- method read(line,phase)
- self$declaration.read(line)
- self.supers := idTaque(":")
- self.supers$parse(line[find(":",line)+1:find("(",line)] | "")
- self.methods:= taque()
- self.text := body()
- while line := readln() do {
- line ? {
- tab(many(white))
- if ="initially" then {
- self.text$read()
- if phase=2 then return
- self.text$delete() # "end" appended manually during writing after
- # generation of the appropriate return value
- return
- } else if ="method" then {
- decl := method(self.name)
- decl$read(line,phase)
- self.methods$insert(decl,decl$name())
- } else if ="end" then {
- # "end" is tossed here. see "initially" above
- return
- } else if ="procedure" then {
- decl := Procedure("")
- decl$read(line,phase)
- /self.glob := []
- put(self.glob,decl)
- } else if ="global" then {
- /self.glob := []
- put(self.glob,Global(line))
- } else if ="record" then {
- /self.glob := []
- put(self.glob,declaration(line))
- } else if upto(nonwhite) then {
- halt("class/read expected declaration on: ",line)
- }
- }
- }
- halt("class/read syntax error: eof inside a class definition")
- end
-
- #
- # Miscellaneous methods on classes
- #
- method has_initially()
- return $*self.text > 0
- end
- method ispublic(fieldname)
- if self.fields$ispublic(fieldname) then return fieldname
- end
- method foreachmethod()
- suspend $!self.methods
- end
- method foreachsuper()
- suspend $!self.supers
- end
- method foreachfield()
- suspend $!self.fields
- end
- method transitive_closure()
- count := $*self.supers
- while count > 0 do {
- added := taque()
- every sc := $!self.supers do {
- if /(super := classes$lookup(sc)) then
- halt("class/transitive_closure: couldn't find superclass ",sc)
- every supersuper := super$foreachsuper() do {
- if / self.supers$lookup(supersuper) &
- /added$lookup(supersuper) then {
- added$insert(supersuper)
- }
- }
- }
- count := $*added
- every self.supers$insert($!added)
- }
- end
- #
- # write the class declaration: if s is "class" write as a spec
- # otherwise, write as a constructor
- #
- method writedecl(f,s)
- writes(f, s," ",self.name)
- if s=="class" & ( *(supers := self.supers$String()) > 0 ) then
- writes(f," : ",supers)
- writes(f,"(")
- rv := self.fields$String(s)
- if *rv > 0 then rv ||:= ","
- if s~=="class" & \self.ifields then # inherited fields
- every l := !self.ifields do rv ||:= l.ident || ","
- writes(f,rv[1:-1])
- write(f,,")")
- end
- method writespec(f) # write the specification of a class
- f := envopen(filename(self.name),"w")
- self$writedecl(f,"class")
- every ($!self.methods)$writedecl(f,"method")
- if self$has_initially() then write(f,"initially")
- write(f,"end")
- close(f)
- end
-
- #
- # write out the Icon code for this class' explicit methods
- # and its "nested global" declarations (procedures, records, etc.)
- #
- method writemethods()
- f:= envopen(filename(self.name)||".icn","w")
- every ($!self.methods)$write(f,self.name)
-
- if \self.glob & *self.glob>0 then {
- write(f,"#\n# globals declared within the class\n#")
- every i := 1 to *self.glob do (self.glob[i])$write(f,"")
- }
- close(f)
- end
-
- #
- # write - write an Icon implementation of a class to file f
- #
- method write()
- f:= envopen(filename(self.name)||".icn","a")
- #
- # must have done inheritance computation to write things out
- #
- if /self.ifields then self$resolve()
-
- #
- # write a record containing the state variables
- #
- writes(f,"record ",self.name,"_state(__state,__methods") # reserved fields
- rv := ","
- rv ||:= self.fields$idTaque.String() # my fields
- if rv[-1] ~== "," then rv ||:= ","
- every s := (!self.ifields).ident do rv ||:= s || "," # inherited fields
- write(f,rv[1:-1],")")
-
- #
- # write a record containing the methods
- #
- writes(f,"record ",self.name,"_methods(")
- rv := ""
-
- every s := ((($!self.methods)$name()) | # my explicit methods
- self.fields$foreachpublic() | # my implicit methods
- (!self.imethods).ident | # my inherited methods
- $!self.supers) # super.method fields
- do rv ||:= s || ","
-
- if *rv>0 then rv[-1] := "" # trim trailling ,
- write(f,rv,")")
-
- #
- # write a global containing this classes' operation record
- # along with declarations for all superclasses op records
- #
- writes(f,"global ",self.name,"__oprec")
- every writes(f,", ", $!self.supers,"__oprec")
- write(f)
-
- #
- # write the constructor procedure.
- # This is a long involved process starting with writing the declaration.
- #
- self$writedecl(f,"procedure")
- write(f,"local self,clone")
-
- #
- # initialize operation records for this and superclasses
- #
- write(f,"initial {\n",
- " if /",self.name,"__oprec then ",self.name,"initialize()")
- if $*self.supers > 0 then
- every (super <- $!self.supers) ~== self.name do
- write(f," if /",super,"__oprec then ",super,"initialize()\n",
- " ",self.name,"__oprec.",super," := ", super,"__oprec")
- write(f," }")
-
- #
- # create self, initialize from constructor parameters
- #
- writes(f," self := ",self.name,"_state(&null,",self.name,"__oprec")
- every writes(f,",",$!self.fields)
- if \self.ifields then every writes(f,",",(!self.ifields).ident)
- write(f,")\n self.__state := self")
-
- #
- # call my own initially section, if any
- #
- if $*self.text > 0 then write(f," ",self.name,"initially(self)")
-
- #
- # call superclasses' initially sections
- #
- if $*self.supers > 0 then {
- every (super <- $!self.supers) ~== self.name do {
- if (classes$lookup(super))$has_initially() then {
- if /madeclone := 1 then {
- write(f," clone := ",self.name,"_state()\n",
- " clone.__state := clone\n",
- " clone.__methods := ",self.name,"__oprec")
- }
- write(f," # inherited initialization from class ",super)
- write(f," every i := 2 to *self do clone[i] := self[i]\n",
- " ",super,"initially(clone)")
- every l := !self.ifields do {
- if l.class == super then
- write(f," self.",l.ident," := clone.",l.ident)
- }
- }
- }
- }
-
- #
- # return the pair that comprises the object:
- # a pointer to the instance (__mystate), and
- # a pointer to the class operation record
- #
- write(f," return idol_object(self,",self.name,"__oprec)\n",
- "end\n")
-
- #
- # write out class initializer procedure to initialize my operation record
- #
- write(f,"procedure ",self.name,"initialize()")
- writes(f," initial ",self.name,"__oprec := ",self.name,"_methods")
- rv := "("
- every s := ($!self.methods)$name() do { # explicit methods
- if *rv>1 then rv ||:= ","
- rv ||:= self.name||s
- }
- every me := self.fields$foreachpublic() do { # implicit methods
- if *rv>1 then rv ||:= "," # (for public fields)
- rv ||:= self.name||me
- }
- every l := !self.imethods do { # inherited methods
- if *rv>1 then rv ||:= ","
- rv ||:= l.class||l.ident
- }
- write(f,rv,")\n","end")
- #
- # write out initially procedure, if any
- #
- if self$has_initially() then {
- write(f,"procedure ",self.name,"initially(self)")
- self.text$write(f)
- write(f,"end")
- }
-
- #
- # write out implicit methods for public fields
- #
- every me := self.fields$foreachpublic() do {
- write(f,"procedure ",self.name,me,"(self)")
- if \strict then {
- write(f," if type(self.",me,") == ",
- "(\"list\"|\"table\"|\"set\"|\"record\") then\n",
- " runerr(501,\"idol: scalar type expected\")")
- }
- write(f," return .(self.",me,")")
- write(f,"end")
- write(f)
- }
-
- close(f)
-
- end
-
- #
- # resolve -- primary inheritance resolution utility
- #
- method resolve()
- #
- # these are lists of [class , ident] records
- #
- self.imethods := []
- self.ifields := []
- ipublics := []
- addedfields := table()
- addedmethods := table()
- every sc := $!self.supers do {
- if /(superclass := classes$lookup(sc)) then
- halt("class/resolve: couldn't find superclass ",sc)
- every superclassfield := superclass$foreachfield() do {
- if /self.fields$lookup(superclassfield) &
- /addedfields[superclassfield] then {
- addedfields[superclassfield] := superclassfield
- put ( self.ifields , classident(sc,superclassfield) )
- if superclass$ispublic(superclassfield) then
- put( ipublics, classident(sc,superclassfield) )
- } else if \strict then {
- warn("class/resolve: '",sc,"' field '",superclassfield,
- "' is redeclared in subclass ",self.name)
- }
- }
- every superclassmethod := (superclass$foreachmethod())$name() do {
- if /self.methods$lookup(superclassmethod) &
- /addedmethods[superclassmethod] then {
- addedmethods[superclassmethod] := superclassmethod
- put ( self.imethods, classident(sc,superclassmethod) )
- }
- }
- every public := (!ipublics) do {
- if public.class == sc then
- put (self.imethods, classident(sc,public.ident))
- }
- }
- end
- end
-
- #
- # a class defining operations on methods and procedures
- #
- class method : declaration (class,text)
- method read(line,phase)
- self$declaration.read(line)
- self.text := body()
- if phase = 1 then
- self.text$read()
- end
- method writedecl(f,s)
- decl := self$String()
- if s == "method" then decl[1:upto(white,decl)] := "method"
- else {
- decl[1:upto(white,decl)] := "procedure"
- decl[upto(white,decl)] ||:= self.class
- if *self.class ~= 0 then {
- i := find("(",decl)
- decl[i] ||:= "self" || (((decl[i+1] ~== ")"), ",") | "")
- }
- }
- write(f,decl)
- end
- method write(f)
- if self.name ~== "initially" then
- self$writedecl(f,"procedure")
- self.text$write(f)
- self.text := &null # after writing out text, forget it!
- end
- end
-
- #
- # A class for ordinary Icon global declarations
- #
- class Global(s)
- method write(f)
- write(f,self.s)
- end
- end
-
- #
- # a class corresponding to an Icon table, with special treatment of empties
- #
- class Table(t)
- method size()
- return (* \ self.t) | 0
- end
- method insert(x,key)
- /self.t := table()
- /key := x
- if / (self.t[key]) := x then return
- end
- method lookup(key)
- if t := \self.t then return t[key]
- return
- end
- method foreach()
- if t := \self.t then every suspend !self.t
- end
- end
-
- #
- # tabular queues (taques):
- # a class defining objects which maintain synchronized list and table reps
- # Well, what is really provided are loosely-coordinated list/tables
- #
- class taque : Table (l)
- method insert(x,key)
- /self.l := []
- if self$Table.insert(x,key) then put(self.l,x)
- end
- method foreach()
- if l := \self.l then every suspend !self.l
- end
- method insert_t(x,key)
- self$Table.insert(x,key)
- end
- method foreach_t()
- suspend self$Table.foreach()
- end
- end
-
- #
- # support for taques found as lists of ids separated by punctuation
- # constructor called with (separation char, source string)
- #
- class idTaque : taque(punc)
- method parse(s)
- s ? {
- tab(many(white))
- while name := tab(find(self.punc)) do {
- self$insert(trim(name))
- move(1)
- tab(many(white))
- }
- if any(nonwhite) then self$insert(trim(tab(0)))
- }
- return
- end
- method String()
- if /self.l then return ""
- out := ""
- every id := !self.l do out ||:= id||self.punc
- return out[1:-1]
- end
- end
-
- #
- # parameter lists in which the final argument may have a trailing []
- #
- class argList : idTaque(public varg)
- method insert(s)
- if \self.varg then halt("variable arg must be final")
- if i := find("[",s) then {
- if not (j := find("]",s)) then halt("variable arg expected ]")
- s[i : j+1] := ""
- self.varg := s := trim(s)
- }
- self$idTaque.insert(s)
- end
- method String()
- return self$idTaque.String() || ((\self.varg & "[]") | "")
- end
- initially
- self.punc := ","
- end
-
- #
- # Idol class field lists in which fields may be preceded by a "public" keyword
- #
- class classFields : argList(publics)
- method String(s)
- if *(rv := self$argList.String()) = 0 then return ""
- if /s | (s ~== "class") then return rv
- if self$ispublic(self.l[1]) then rv := "public "||rv
- every field:=self$foreachpublic() do rv[find(","||field,rv)] ||:= "public "
- return rv
- end
- method foreachpublic()
- if \self.publics then every suspend !self.publics
- end
- method ispublic(s)
- if \self.publics then every suspend !self.publics == s
- end
- method insert(s)
- s ? {
- if ="public" & tab(many(white)) then {
- s := tab(0)
- /self.publics := []
- put(self.publics,s)
- }
- }
- self$argList.insert(s)
- end
- initially
- self.punc := ","
- end
-
- #
- # tell whether the character following s is within a quote or not
- #
- procedure notquote(s)
- quotes := 0
- outs := ""
- # this is a bug for people who write code like \"hello"...
- s ? {
- while outs ||:= tab(find("\\")+1) do { move(1) }
- outs ||:= tab(0)
- }
- s := outs
- outs := ""
- s ? {
- while outs ||:= tab(find("\""|"'")+1) do {
- quotes +:= 1
- if tab(find(outs[-1])) then {
- quotes +:= 1
- move(1)
- }
- }
- }
- if quotes % 2 = 0 then return
- end
-
- #
- # filter the input translating $ references
- # (also eats comments and trims lines)
- #
- procedure readln()
- count := 0
- if line := read(fin) then {
- fLine +:= 1
- line[ 1(x<-find("#",line),notquote(line[1:x])) : 0] := ""
- line := trim(line)
- while ((x := find("$",line)) & notquote(line[1:x])) do {
- z := line[x+1:0] ||" " # " " is for bal()
- if find(line[x+1],"!*@?") then { # Invocation operators $! $* $@ $?
- z ? {
- move(1)
- tab(many(white))
- if not (id := tab(many(alphadot))) then {
- if not match("(") then halt("readline can't parse ",line)
- if not (id := tab(&pos<bal())) then
- halt("readline: cant bal ",&subject)
- }
- case line[x+1] of {
- "@": Op := "activate"
- "*": Op := "size"
- "!": Op := "foreach"
- "?": Op := "random"
- default: halt("readline: unknown operator $",line[x+1])
- }
- count +:= 1
- line[x:0] :=
- "(__self"||count||" := "||id||").__methods."||
- Op||"(__self"||count||".__state)"||tab(0)
- }
- } else {
- reverse(line[1:x])||" " ? {
- tab(many(white))
- if not (id := reverse(tab(many(alphadot)))) then {
- if not match(")") then halt("readline: can't parse")
- if not (id := reverse(tab(&pos<bal(&cset,')','('))))
- then halt("readline: can't bal ",&subject)
- }
- nummatched := &pos-1
- }
- if not (lp := find("(",z)) then halt("readline: expected '('")
- if z[lp+1] ~== ")" then c:="," else c:=""
- count +:= 1
- line[x-nummatched : x+lp+1] :=
- "(__self"||count||" := "||id||").__methods."||
- z[1:lp+1]||"__self"||count||".__state"||c
- }
- }
- return line
-
-
- } else fail
- end
-
- #
- # procedure to read a single Idol source file
- #
- procedure readinput(name,phase)
- if \loud then write("\t",name)
- fName := name
- fLine := 0
- fin := sysopen(name,"r")
- while line := readln() do {
- line ? {
- tab(many(white))
- if ="class" then {
- decl := class()
- decl$read(line,phase)
- if phase=1 then {
- decl$writemethods()
- classes$insert(decl,decl$name())
- } else classes$insert_t(decl,decl$name())
- }
- else if ="procedure" then {
- if comp = 0 then comp := 1
- decl := method("")
- decl$read(line,phase)
- decl$write(fout,"")
- }
- else if ="record" then {
- if comp = 0 then comp := 1
- decl := declaration(line)
- decl$write(fout,"")
- }
- else if ="global" then {
- if comp = 0 then comp := 1
- decl := Global(line)
- decl$write(fout,"")
- }
- else if ="method" then {
- halt("readinput: method outside class")
- }
- }
- }
- close(fin)
- end
-
- #
- # error/warning/message handling
- #
- procedure halt(args[])
- errsrc()
- every writes(&errout,!args)
- stop()
- end
-
- procedure warn(args[])
- errsrc()
- every writes(&errout,!args)
- write(&errout)
- end
-
- procedure errsrc()
- writes(&errout,"\"",\fName,"\", line ",\fLine,": Idol/")
- end
- #
- # System-independent, but system related routines
- #
- procedure tryopen(file,mode)
- if f := open(file,mode) then return close(f)
- end
- procedure tryenvopen(file,mode)
- return tryopen(envpath(file),mode)
- end
- procedure sysopen(file,mode)
- if not (f := open(file,mode)) then
- halt("Couldn't open file ",file," for mode ",mode)
- return f
- end
- procedure envopen(file,mode)
- return sysopen(envpath(file),mode)
- end
- procedure writelink(s)
- write(fout,"link \"",s,"\"")
- end
- procedure icont(argstr,prefix)
- static s
- initial { s := (getenv("ICONT")|"icont") }
- return mysystem(\prefix||s||argstr | s||argstr)
- end
-